package com.disruptor.demo.producer;

import com.disruptor.demo.event.SeckillOrderEvent;
import com.lmax.disruptor.RingBuffer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * 秒杀订单事件生产者
 * 角色定位：秒杀流程的"入口转换器"，负责将用户下单请求（如HTTP请求）转换为Disruptor可处理的事件，
 * 是连接业务层（用户请求）与Disruptor事件系统的核心桥梁。
 * 核心优势：基于Disruptor的无锁序列号机制，支持高并发下安全发布事件，无锁竞争开销。
 */
@Slf4j
@Component
public class SeckillOrderProducer {
    /**
     * Disruptor核心事件缓冲区（RingBuffer）
     * 作用：存储预创建的SeckillOrderEvent事件，生产者通过它申请序列号、填充数据并发布事件。
     * 注入方式：构造器注入（由DisruptorConfig初始化后注入），确保RingBuffer初始化完成后再使用，避免空指针。
     */
    private final RingBuffer<SeckillOrderEvent> ringBuffer;

    /**
     * 秒杀商品ID
     * 配置来源：从application.yml的seckill.goods-id注入（如"SKU_001"），
     * 设计目的：统一秒杀商品标识，避免硬编码，支持多商品秒杀扩展（可改为入参动态传递）。
     */
    @Value("${seckill.goods-id}")
    private String seckillGoodsId;

    /**
     * 构造器：注入RingBuffer（依赖注入，确保生产者与Disruptor缓冲区强绑定）
     * 注意：RingBuffer是线程安全的，多生产者场景下可共享此实例，无需额外加锁。
     *
     * @param ringBuffer Disruptor初始化后的事件缓冲区实例
     */
    public SeckillOrderProducer(RingBuffer<SeckillOrderEvent> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    /**
     * 发布秒杀订单事件（用户下单请求的核心处理入口）
     * 调用场景：用户发起秒杀下单请求时（如Controller层调用），将请求参数转换为事件并发布到Disruptor。
     * 核心保障：无锁化操作，确保高并发（10万+TPS）下事件发布安全，无线程阻塞。
     *
     * @param userId   下单用户ID（如"USER_123"，需与用户系统唯一标识一致）
     * @param buyCount 商品购买数量（秒杀场景通常限制为1，此处支持动态传参，便于业务扩展）
     */
    public void publishSeckillOrder(String userId, int buyCount) {
        try {
            // 1. 无锁申请下一个事件序列号（核心步骤，Disruptor高性能关键）
            // 原理：基于CAS（Compare-And-Swap）操作申请序列号，多生产者并发申请时无锁竞争，
            // 若当前序列号已被占用，会自动重试直到申请成功，避免传统锁的阻塞开销。
            long sequence = ringBuffer.next();

            try {
                // 2. 获取序列号对应的事件对象（预创建机制，无GC开销）
                // 关键优化：RingBuffer初始化时已通过SeckillOrderEventFactory预创建所有事件对象，
                // 此处仅复用已有对象，无需new创建，彻底避免运行时GC（减少GC停顿对性能的影响）。
                SeckillOrderEvent event = ringBuffer.get(sequence);

                // 3. 填充事件数据（为复用的事件对象赋值，覆盖旧数据）
                event.setOrderId(UUID.randomUUID().toString().replace("-", "")); // 生成无横杠UUID，确保订单唯一
                event.setUserId(userId); // 绑定下单用户
                event.setGoodsId(seckillGoodsId); // 绑定秒杀商品
                event.setBuyCount(buyCount); // 设置购买数量
                event.setErrorMsg(null); // 清空错误信息（事件复用，避免残留上一次失败的错误信息）

                //  debug日志：仅记录事件发布关键信息，生产环境可通过日志级别控制（如关闭debug），避免日志量过大
                log.debug("【事件发布】秒杀订单事件已提交到RingBuffer | orderId={} | userId={} | 商品ID={}",
                        event.getOrderId(), userId, seckillGoodsId);

            } finally {
                // 4. 发布事件（必须在finally块中执行，核心安全保障）
                // 原因：若步骤3填充数据时抛出异常，若不执行publish，申请的sequence会一直被占用，
                // 导致RingBuffer缓冲区"卡死"，后续生产者无法申请新序列号。finally确保无论是否异常，都会释放序列号。
                ringBuffer.publish(sequence);
            }

        } catch (Exception e) {
            // 异常处理：捕获事件发布过程中的所有异常（如RingBuffer已满、内存不足等），避免异常向上抛出导致用户请求失败
            log.error("【事件发布失败】用户下单请求处理异常 | userId={} | buyCount={} | 异常原因：{}",
                    userId, buyCount, e.getMessage(), e);

            // 生产环境补充逻辑（兜底策略）：
            // 1. 写入本地失败队列（如LinkedBlockingQueue），后续通过定时任务重试发布；
            // 2. 触发降级逻辑（如返回"系统繁忙，请稍后重试"，避免用户感知异常）；
            // 3. 发送告警（如钉钉/短信），通知运维人员及时处理问题。
        }
    }
}
